﻿using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.Caching;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching.Distributed;
using System.Threading;
using System.Collections.Concurrent;

namespace Helpers
{
    /// <summary>
    /// 内存缓存帮助类
    /// </summary>
    public class MemoryCacheHelper
    {
        private readonly IMemoryCache _memoryCache;
        private static ConcurrentDictionary<string, SemaphoreSlim> _keyValuePairs => _keyValuePairsLazy.Value;
        private static readonly Lazy<ConcurrentDictionary<string, SemaphoreSlim>> _keyValuePairsLazy = new Lazy<ConcurrentDictionary<string, SemaphoreSlim>>(() => new ConcurrentDictionary<string, SemaphoreSlim>());
        public MemoryCacheHelper(IMemoryCache memoryCache)
        {
            this._memoryCache = memoryCache;
        }

        /// <summary>
        /// 线程安全,同一时间只能初始化一个
        /// </summary>
        /// <typeparam name="T">缓存实体类型</typeparam>
        /// <param name="key">关键字</param>
        /// <param name="factory">生产工厂</param>
        /// <param name="absoluteExpirationRelativeToNow">超时时间</param>
        /// <returns>缓存</returns>
        public async Task<T> GetOrCreateAsync<T>(string key, TimeSpan absoluteExpirationRelativeToNow, Func<Task<T>> factory)
        {
            if (_memoryCache.TryGetValue<T>(key, out var value))
            {
                return value;
            }
            else
            {
                var slim = _keyValuePairs.GetOrAdd(key, new SemaphoreSlim(1));
                try
                {
                    await slim.WaitAsync();
                    if (_memoryCache.TryGetValue(key, out value))
                    {
                        return value;
                    }
                    else
                    {
                        return _memoryCache.Set(key, await factory(), absoluteExpirationRelativeToNow);
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    slim.Release();
                }
            }
        }

        /// <summary>
        /// 线程安全,同一时间只能初始化一个
        /// </summary>
        public Task<T> GetOrCreateAsync<T>(string key, TimeSpan absoluteExpirationRelativeToNow, Func<T> factory)
        {
            return GetOrCreateAsync(key, absoluteExpirationRelativeToNow, () => Task.Run(() => factory()));
        }

        /// <summary>
        /// 不是线程安全的,可能会同一时间初始化多个,选择一个返回
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="key"></param>
        /// <param name="factory"></param>
        /// <returns></returns>
        public Task<T> GetOrCreateAsync<T>(string key, Func<ICacheEntry, Task<T>> factory)
        {
            return _memoryCache.GetOrCreateAsync(key, it => factory(it));
        }

        /// <summary>
        /// 不是线程安全的,可能会同一时间初始化多个,选择一个返回
        /// </summary>
        public Task<T> GetOrCreateAsync<T>(string key, Func<ICacheEntry, T> factory)
        {
            return GetOrCreateAsync(key, it => Task.Run(() => factory(it)));
        }

        /// <summary>
        /// 不是线程安全的,可能会同一时间初始化多个,选择一个返回
        /// </summary>
        public T GetOrCreate<T>(string key, Func<ICacheEntry, T> factory)
        {
            return _memoryCache.GetOrCreate(key, factory);
        }

        public T Get<T>(string key)
        {
            return _memoryCache.Get<T>(key);
        }

        public bool TryGetValue<T>(string key, out T value)
        {
            return _memoryCache.TryGetValue<T>(key, out value);
        }

        public T Set<T>(string key, T value, TimeSpan absoluteExpirationRelativeToNow)
        {
            return _memoryCache.Set(key, value, absoluteExpirationRelativeToNow);
        }

        public void Remove(string key)
        {
            _memoryCache.Remove(key);
        }

        #region 限流
        /// <summary>
        /// 限流,限制key在指定时间内访问的次数
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="expiry">过期时间</param>
        /// <param name="limit">上限</param>
        /// <returns>返回是否超过使用限制和当前的使用次数</returns>
        public async Task<long> ThrottleAsync(string key, TimeSpan expiry)
        {
            var result = await GetOrCreateAsync(key, expiry, () =>
            {
                return new Throttle(0);
            });
            return System.Threading.Interlocked.Increment(ref result._count);
            //var count = System.Threading.Interlocked.Increment(ref result._count);
            //return (count <= limit, count);
        }

        public long ThrottleCountAsync(string key)
        {
            return Get<Throttle>(key)?._count ?? 0;
        }

        public long ThrottleClear(string key)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 限流实体类
        /// </summary>
        private class Throttle
        {
            public Throttle(int count)

            {
                _count = count;
            }
            public int _count;
        }
        #endregion
    }

    public class DistributedCacheHelper
    {
        private readonly IDistributedCache _cache;

        //private static readonly IDistributedCache _cache = DiHelper.ServiceProvider.GetRequiredService<IDistributedCache>();
        public DistributedCacheHelper(IDistributedCache cache)
        {
            this._cache = cache;
        }
        public Task<string> GetStringAsync(string key)
        {
            return _cache.GetStringAsync(key);
        }
        public Task SetStringAsync(string key, string value, DistributedCacheEntryOptions options)
        {
            return _cache.SetStringAsync(key, value, options);
        }
        public Task RemoveAsync(string key, string value, DistributedCacheEntryOptions options)
        {
            return _cache.RemoveAsync(key);
        }
        public Task RefreshAsync(string key, string value, DistributedCacheEntryOptions options)
        {
            return _cache.RefreshAsync(key);
        }
        public async Task<string> GetOrCreateAsync(string key, Func<DistributedCacheEntryOptions, Task<string>> factory)
        {
            string value = null;
            if (String.IsNullOrEmpty(value = await _cache.GetStringAsync(key)))
            {
                DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
                value = await factory(options);
                await _cache.SetStringAsync(key, value, options);
            }
            return value;
        }
        public async Task<string> GetOrCreateAsync(string key, Func<DistributedCacheEntryOptions, string> factory)
        {
            string value = null;
            if (String.IsNullOrEmpty(value = await _cache.GetStringAsync(key)))
            {
                DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
                value = factory(options);
                await _cache.SetStringAsync(key, value, options);
            }
            return value;
        }
    }
}
